package com.yboot.starter.shiro.core.realm;

import com.yboot.starter.cache.middle.RedisUtil;
import com.yboot.starter.common.core.constant.CommonConstant;
import com.yboot.starter.common.core.userdetails.LoginUser;
import com.yboot.starter.shiro.core.impl.ShiroUserDetailService;
import com.yboot.starter.common.core.utils.SpringContextUtils;
import com.yboot.starter.shiro.core.utils.JwtUtil;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;

@Slf4j
public class TokenUtils {

    /**
     * 获取 request 里传递的 token
     *
     * @param request
     * @return
     */
    public static String getTokenByRequest(HttpServletRequest request) {
        String token = request.getParameter("token");
        if (token == null) {
            token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
        }
        return token;
    }

    public static boolean verifyToken(HttpServletRequest request) {
        return verifyToken(request,
                (ShiroUserDetailService) SpringContextUtils.getBean("shiroUserDetailService"),
                (RedisUtil) SpringContextUtils.getBean("redisUtil"));
    }

    /**
     * 验证Token
     */
    public static boolean verifyToken(HttpServletRequest request, ShiroUserDetailService shiroUserDetailService, RedisUtil redisUtil) {
        log.debug(" -- url --" + request.getRequestURL());
        String token = getTokenByRequest(request);
        return TokenUtils.verifyToken(token, shiroUserDetailService, redisUtil);
    }

    /**
     * 验证Token
     */
    public static boolean verifyToken(String token, ShiroUserDetailService shiroUserDetailService, RedisUtil redisUtil) {
        if (StringUtils.isBlank(token)) {
            throw new RuntimeException("token不能为空!");
        }

        // 解密获得username，用于和数据库进行对比
        String username = JwtUtil.getUsername(token);
        if (username == null) {
            throw new RuntimeException("token非法无效!");
        }

        // 查询用户信息
        LoginUser user = TokenUtils.getLoginUser(username, shiroUserDetailService, redisUtil);
        //LoginUser user = userDetailService.getUserByName(username);
        if (user == null) {
            throw new RuntimeException("用户不存在!");
        }
        // 判断用户状态
        if (user.isAccountLocked()) {
            throw new RuntimeException("账号已被锁定,请联系管理员!");
        }
        // 校验token是否超时失效 & 或者账号密码是否错误
        if (jwtTokenRefresh(token, username, user.getPassword())) {
            throw new RuntimeException("token已失效");
        }
        return true;
    }

    /**
     * 刷新token（保证用户在线操作不掉线）
     *
     * @param token
     * @param userName
     * @param passWord
     * @return
     */
    private static boolean jwtTokenRefresh(String token, String userName, String passWord) {
        // 校验token有效性
        if (!JwtUtil.verify(token, userName, passWord)) {
            String newAuthorization = JwtUtil.sign(userName, passWord);
            setToken(userName, newAuthorization);
            return true;
        }
        return false;
    }

    /**
     * 获取登录用户
     *
     * @param shiroUserDetailService
     * @param username
     * @return
     */
    public static LoginUser getLoginUser(String username, ShiroUserDetailService shiroUserDetailService, RedisUtil redisUtil) {
        LoginUser loginUser = null;
        String loginUserKey = CommonConstant.SYS_USERS_CACHE + ":" + username;
        //【重要】此处通过redis原生获取缓存用户，是为了解决微服务下system服务挂了，其他服务互调不通问题---
        if (redisUtil.hasKey(loginUserKey)) {
            loginUser = (LoginUser) redisUtil.get(loginUserKey);
        } else {
            // 查询用户信息
            loginUser = shiroUserDetailService.loadByUsername(username);
            redisUtil.set(loginUserKey, loginUser, 60 * 3);
        }
        return loginUser;
    }

    /**
     * 设置token
     */
    public static String setToken(String username, String authToken) {
        RedisUtil redisUtil = (RedisUtil) SpringContextUtils.getBean("redisUtil");
        String key = getTokenKey(username);
        redisUtil.set(key, authToken);
        redisUtil.expire(key, CommonConstant.TOKEN_EXPIRE_TIME);
        return authToken;
    }

    /**
     * token的缓存key
     * @param username
     * @return
     */
    public static String getTokenKey(String username) {
        return CommonConstant.PREFIX_USER_TOKEN + username;
    }

    /**
     * 获取token
     */
    public static String getToken(String username) {
        RedisUtil redisUtil = (RedisUtil) SpringContextUtils.getBean("redisUtil");
        String key = getTokenKey(username);
        String token = redisUtil.get(key);
        return token;
    }
}
